1 Annotating the PPIs with land use parameters

In this study we aim to calculate ‘representative’ radar cross sections for birds aloft, based on the take-off habitat, which is probably a good indicator for the bird species/species groups measured by the radar. In this notebook we will classify land use and calculate the distance to the nearest urbanised areas for each of the PPI ‘pixels’.

The land use is based on the CORINE Land Cover dataset and specifically the 2018 version (CLC2018), which should be most relevant for the 2017-2018 fireworks event.

1.1 Setting up the annotation environment

library(raster)
library(sf)
library(dplyr)
library(ggplot2)
library(ggpubr)
library(gridExtra)
library(viridis)
library(mapview)

1.2 Converting the land use map

To start, we need to convert the land use map to the same 1) resolution, and 2) extent of the radar PPIs as we can then simply ‘overlay’ both rasters on top of each other and do calculations. We load the PPIs and extract the CRS information contained in the proj4 strings.

ppi_hrw <- readRDS("data/processed/corrected_ppi_hrw.RDS")
ppi_dhl <- readRDS("data/processed/corrected_ppi_dhl.RDS")

ppi_proj4_hrw <- ppi_hrw$data@proj4string
ppi_proj4_dhl <- ppi_dhl$data@proj4string

And we load and prepare the land use map it’s all about. To aid the classification process, we also load the land use classes contained in the entire CLC2018 dataset, otherwise the classes will remain anonymous numbers.

landuse <- raster("data/raw/landuse/clc2018_clc2018_v2018_20_raster100m/CLC2018_CLC2018_V2018_20.tif")
landuse_classes <- read.csv("data/raw/landuse/clc2018_clc2018_v2018_20_raster100m/Legend/CLC2018_CLC2018_V2018_20_QGIS.txt",
                            col.names = c("landuse_id", "r", "g", "b", "x", "landuse_class"))[, c("landuse_id", "landuse_class")]

1.2.1 Cropping the land use map

As the CLC2018 dataset is so large it does not fit in memory at all in the steps below, so we have to crop the raster dataset for further processing. Even then, it still requires a beefy computer to process these files. First we calculate a bounding box for the landuse raster based on the bounding boxes of the radar data.

padding <- 25000  # Padding in m to make sure we crop out of the land use map with a wide margin to compensate for edge-effects later
bbox_meters <- abs(ppi_dhl$data@bbox[[1]]) + padding  # Assuming the PPI range of DHL and HRW are the same

bbox_hrw <- st_bbox(c(xmin = -bbox_meters, ymin = -bbox_meters, xmax = bbox_meters, ymax = bbox_meters), crs = ppi_proj4_hrw)
bbox_dhl <- st_bbox(c(xmin = -bbox_meters, ymin = -bbox_meters, xmax = bbox_meters, ymax = bbox_meters), crs = ppi_proj4_dhl)

bbox_hrw %>%
  st_as_sfc() %>%
  st_transform(crs(landuse)) %>%
  st_bbox -> bbox_landuse_hrw

bbox_dhl %>%
  st_as_sfc() %>%
  st_transform(crs(landuse)) %>%
  st_bbox -> bbox_landuse_dhl

We can now crop and plot the land use maps centered on the radar sites, with a 2.510^{4} meter padding surrounding the extent of the radar data.

ext_hrw <- extent(c(bbox_landuse_hrw[1], bbox_landuse_hrw[3], bbox_landuse_hrw[2], bbox_landuse_hrw[4]))
ext_dhl <- extent(c(bbox_landuse_dhl[1], bbox_landuse_dhl[3], bbox_landuse_dhl[2], bbox_landuse_dhl[4]))

landuse_crop_hrw <- crop(landuse, ext_hrw)
landuse_crop_dhl <- crop(landuse, ext_dhl)

And plot the result:

par(pty = "s", mfrow = c(1, 2))
image(landuse_crop_hrw, main = "Herwijnen")
image(landuse_crop_dhl, main = "Den Helder")

Apparantly large swathes of the North Sea are set to NaN, so we better convert those to ‘Sea and ocean’ as well.

sea_id <- match('Sea and ocean', landuse_classes$landuse_class)
landuse_crop_hrw[is.na(landuse_crop_hrw)] <- landuse_classes[sea_id, "landuse_id"]
landuse_crop_dhl[is.na(landuse_crop_dhl)] <- landuse_classes[sea_id, "landuse_id"]

par(pty = "s", mfrow = c(1, 2))
image(landuse_crop_hrw, main = "Herwijnen")
image(landuse_crop_dhl, main = "Den Helder")

1.2.2 Reprojecting the land use map

Now that the land use map is cropped, we can start the reprojecting to the CRS of the radar PPI. As we’re dealing with categorical data, we set method = "ngb" to use nearest neighbour interpolation.

landuse_hrw_reprojected <- projectRaster(landuse_crop_hrw, crs = ppi_proj4_hrw, method = "ngb")
landuse_dhl_reprojected <- projectRaster(landuse_crop_dhl, crs = ppi_proj4_dhl, method = "ngb")
levels(landuse_hrw_reprojected) <- levels(landuse_crop_hrw)
levels(landuse_dhl_reprojected) <- levels(landuse_crop_dhl)

If the reprojection went successful, the CRS of the reprojected land use map and the radar PPI should be the same.

compareCRS(ppi_hrw$data@proj4string, landuse_hrw_reprojected@crs)
## [1] TRUE
compareCRS(ppi_dhl$data@proj4string, landuse_dhl_reprojected@crs)
## [1] TRUE

Apparently that is the case.

1.2.3 Resampling the land use map to a lower resolution

The cellsize of the PPIs is 500, 500x500, 500 meters, but the land use map is much more finely detailed (~100x100m), so we need to resample the latter to derive a land use map at a 500, 500x500, 500 meter resolution as well.

Simple nearest neighbour resampling was tested, but resulted in large parts of fairly small water bodies and especially the rivers ‘disappearing’ from the map, whereas these represent important habitats for birds to forage and roost. So instead, majority resampling, tweaked to retain more of the aforementioned water features was used to resize the land use raster to an appropriate resolution. The tweak constituted of setting the values of water courses and water bodies to the maximum value (total coverage of the assessed window) if half or more of the assessed window contained pixels classified as water courses or water bodies. The quality of this resampling was assessed visually.

majority_resampling <- function(raster, reference_raster, overwrite = FALSE) {
  values <- c(sort(unique(getValues(raster))))
  
  # classes: multidimensional logical array for the classes contained within the land use map
  classes <- layerize(raster, filename = paste("data/processed/landuse/layerize/", substitute(raster), sep = ""),
                      format = "raster", bylayer = TRUE, classes = values, overwrite = overwrite)
  # factor: nr of cells in both horizontal and vertical direction to aggregate
  factor <- round(dim(raster)[1:2] / dim(reference_raster)[1:2])
  # agg: aggregated version of classes (aggregation factor defined by factor) containing max values per class, so 1 if a class is present
  agg <- aggregate(classes, factor, na.rm = TRUE, fun = max)
  # x: resampled agg, now roughly containing land coverage percentages. Method must be bilinear, otherwise arbitrary choices keep being made.
  x <- resample(agg, reference_raster)

  # Tweak the importance of water courses and bodies
  water_courses_id <- match(511, values)
  water_bodies_id <- match(512, values)
  x[[water_courses_id]][x[[water_courses_id]] >= 0.5] <- 1.1
  x[[water_bodies_id]][x[[water_bodies_id]] >= 0.5] <- 1.1

  # Flatten x in y
  y <- which.max(x)

  # Reclassify back to original values
  values <- data.frame(seq(1, length(values)), values)
  colnames(values) <- c("is", "becomes")
  y <- reclassify(y, values)

  return(y)
}
landuse_hrw <- majority_resampling(landuse_hrw_reprojected, as(ppi_hrw$data, "RasterLayer"), overwrite = TRUE)
landuse_dhl <- majority_resampling(landuse_dhl_reprojected, as(ppi_dhl$data, "RasterLayer"), overwrite = TRUE)
writeRaster(landuse_hrw, "data/processed/landuse/landuse_hrw_500m.tif", overwrite = TRUE)
writeRaster(landuse_dhl, "data/processed/landuse/landuse_dhl_500m.tif", overwrite = TRUE)

By now the resampled land use raster should be very similar to the PPI raster, with the exception of — of course — the values contained within.

compareRaster(landuse_hrw, as(ppi_hrw$data, "RasterLayer"), extent = TRUE, rowcol = TRUE, crs = TRUE, res = TRUE, orig = TRUE)
## [1] TRUE
compareRaster(landuse_dhl, as(ppi_dhl$data, "RasterLayer"), extent = TRUE, rowcol = TRUE, crs = TRUE, res = TRUE, orig = TRUE)
## [1] TRUE

Ok, let’s save a copy of what we have so far.

saveRDS(landuse_hrw, "data/processed/landuse/landuse_hrw.RDS")
saveRDS(landuse_dhl, "data/processed/landuse/landuse_dhl.RDS")

1.3 Adding land use classifications to the PPIs

With the land use rasters overlapping exactly with the PPIs, we can simply extract the values of the resampled land use rasters and add these as additional parameters to the PPIs. Besides the code used, we will also add a description of the codes.

landuse_hrw <- readRDS("data/processed/landuse/landuse_hrw.RDS")
landuse_dhl <- readRDS("data/processed/landuse/landuse_dhl.RDS")

values_hrw <- as.data.frame(landuse_hrw@data@values)
values_dhl <- as.data.frame(landuse_dhl@data@values)

ppi_hrw$data$landuse <- as.numeric(unlist(values_hrw))
ppi_dhl$data$landuse <- as.numeric(unlist(values_dhl))

ppi_hrw$data@data <- ppi_hrw$data@data %>%
  left_join(landuse_classes, by = c("landuse" = "landuse_id"))
ppi_hrw$data$ID <- NULL

ppi_dhl$data@data <- ppi_dhl$data@data %>%
  left_join(landuse_classes, by = c("landuse" = "landuse_id"))
ppi_dhl$data$ID <- NULL

1.4 Classify land use types in Urban vs. Rural

As we are interested in the effect urbanization has on the take-off decision of birds during the fireworks event, it is useful to categorise the land use types we have found so far as either ‘urban’ or ‘rural’ as well.

urban_hrw <- ppi_hrw$data@data$landuse < 200
ppi_hrw$data$type <- rep("rural", nrow(ppi_hrw$data))
ppi_hrw$data$type[urban_hrw] <- "urban"
ppi_hrw$data$type <- as.factor(ppi_hrw$data$type)

urban_dhl <- ppi_dhl$data@data$landuse < 200
ppi_dhl$data$type <- rep("rural", nrow(ppi_dhl$data))
ppi_dhl$data$type[urban_dhl] <- "urban"
ppi_dhl$data$type <- as.factor(ppi_dhl$data$type)

1.5 Calculate distance to nearest urban area

For every cell on the raster that is not a cell we have just classified as ‘urban’ we will calculate the distance (in meters) to the nearest cell classified as ‘urban’.

urban_hrw <- ppi_hrw$data[, , "type", drop = FALSE]
urban_hrw$type[urban_hrw$type == "rural"] <- NaN
distance_to_urban_hrw <- distance(as(urban_hrw, 'RasterLayer'))

urban_dhl <- ppi_dhl$data[, , "type", drop = FALSE]
urban_dhl$type[urban_dhl$type == "rural"] <- NaN
distance_to_urban_dhl <- distance(as(urban_dhl, 'RasterLayer'))

writeRaster(distance_to_urban_hrw, "data/processed/landuse/dist_urban_hrw_500m.tif", overwrite = TRUE)
writeRaster(distance_to_urban_dhl, "data/processed/landuse/dist_urban_dhl_500m.tif", overwrite = TRUE)

And we add these values to the PPIs again.

values_dist_urban_hrw <- as.data.frame(distance_to_urban_hrw@data@values)
values_dist_urban_dhl <- as.data.frame(distance_to_urban_dhl@data@values)

ppi_hrw$data$dist_urban <- as.numeric(unlist(values_dist_urban_hrw))
ppi_dhl$data$dist_urban <- as.numeric(unlist(values_dist_urban_dhl))

1.6 Testing

Let’s make a few plots to see if what we have now gathered as additional information makes any sense

1.6.1 Plotting total VIR per land use type

data_hrw <- ppi_hrw$data@data %>%
  filter(landuse_class != "Land principally occupied by agriculture with significant areas of natural vegetation" & landuse_class != "Sea and ocean") %>%
  group_by(landuse_class) %>%
  add_count() %>%
  mutate(total_landuse_class = sum(vir), total_prop_landuse_class = sum(vir) / n)

data_dhl <- ppi_dhl$data@data %>%
  filter(landuse_class != "Land principally occupied by agriculture with significant areas of natural vegetation" & landuse_class != "Sea and ocean") %>%
  group_by(landuse_class) %>%
  add_count() %>%
  mutate(total_landuse_class = sum(vir), total_prop_landuse_class = sum(vir) / n)

ggplot(data_hrw, aes(landuse_class, total_prop_landuse_class, fill = type)) + geom_col() + coord_flip() + labs(title = "Herwijnen")

ggplot(data_dhl, aes(landuse_class, total_prop_landuse_class, fill = type)) + geom_col() + coord_flip() + labs(title = "Den Helder")

1.6.2 Plotting a distance to urban areas effect for VIR

ggplot(ppi_dhl$data@data, aes(x = dist_urban, y = vir, colour = landuse_class)) + 
  geom_point(alpha = 0.5) + xlim(c(0, 10000)) + ylim(c(0, 500000)) + theme(legend.position = "none")

ggplot(ppi_hrw$data@data, aes(x = dist_urban, y = vir, colour = landuse_class)) + 
  geom_point(alpha = 0.5) + xlim(c(0, 10000)) + ylim(c(0, 500000)) + theme(legend.position = "none")

1.6.3 Plotting it all on an interactive map

1.6.3.1 Interactive map of Herwijnen

mapview(ppi_hrw$data[, , c("landuse", "dist_urban")], alpha.regions = 0.6, col.regions = inferno, maxpixels=2000000,
        na.color = "#00000000", map.types = c("CartoDB.Positron", "CartoDB.DarkMatter", "Esri.WorldImagery"),
        layer.name = c("landuse", "dist_urban"))

1.6.3.2 Interactive map of Den Helder

mapview(ppi_dhl$data[, , c("landuse", "dist_urban")], alpha.regions = 0.6, col.regions = inferno, maxpixels=2000000,
        na.color = "#00000000", map.types = c("CartoDB.Positron", "CartoDB.DarkMatter", "Esri.WorldImagery"),
        layer.name = c("landuse", "dist_urban"))
LS0tCnRpdGxlOiAiRmlyZXdvcmtzIENhc2Utc3R1ZHkiCmF1dGhvcjogIkJhcnQgSG9la3N0cmEiCmJpYmxpb2dyYXBoeTogZmlyZXdvcmtzLmJpYgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBrYWJsZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBzbWFydDogdHJ1ZQpmdWxsX3JlcHJvOiAiYHIgZnVsbF9yZXBybyA8LSBGQUxTRWAiCi0tLQoKIyBBbm5vdGF0aW5nIHRoZSBQUElzIHdpdGggbGFuZCB1c2UgcGFyYW1ldGVycwoKSW4gdGhpcyBzdHVkeSB3ZSBhaW0gdG8gY2FsY3VsYXRlICdyZXByZXNlbnRhdGl2ZScgcmFkYXIgY3Jvc3Mgc2VjdGlvbnMgZm9yIGJpcmRzIGFsb2Z0LCBiYXNlZCBvbiB0aGUgdGFrZS1vZmYgaGFiaXRhdCwgd2hpY2ggaXMgcHJvYmFibHkgYSBnb29kIGluZGljYXRvciBmb3IgdGhlIGJpcmQgc3BlY2llcy9zcGVjaWVzIGdyb3VwcyBtZWFzdXJlZCBieSB0aGUgcmFkYXIuIEluIHRoaXMgbm90ZWJvb2sgd2Ugd2lsbCBjbGFzc2lmeSBsYW5kIHVzZSBhbmQgY2FsY3VsYXRlIHRoZSBkaXN0YW5jZSB0byB0aGUgbmVhcmVzdCB1cmJhbmlzZWQgYXJlYXMgZm9yIGVhY2ggb2YgdGhlIFBQSSAncGl4ZWxzJy4KClRoZSBsYW5kIHVzZSBpcyBiYXNlZCBvbiB0aGUgW0NPUklORSBMYW5kIENvdmVyXShodHRwczovL2xhbmQuY29wZXJuaWN1cy5ldS9wYW4tZXVyb3BlYW4vY29yaW5lLWxhbmQtY292ZXIpIGRhdGFzZXQgYW5kIHNwZWNpZmljYWxseSB0aGUgWzIwMTggdmVyc2lvbl0oaHR0cHM6Ly9sYW5kLmNvcGVybmljdXMuZXUvcGFuLWV1cm9wZWFuL2NvcmluZS1sYW5kLWNvdmVyL2NsYzIwMTgpIChgQ0xDMjAxOGApLCB3aGljaCBzaG91bGQgYmUgbW9zdCByZWxldmFudCBmb3IgdGhlIDIwMTctMjAxOCBmaXJld29ya3MgZXZlbnQuCgojIyBTZXR0aW5nIHVwIHRoZSBhbm5vdGF0aW9uIGVudmlyb25tZW50CgpgYGB7ciBzZXR1cCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkoc2YpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkobWFwdmlldykKYGBgCgojIyBDb252ZXJ0aW5nIHRoZSBsYW5kIHVzZSBtYXAKClRvIHN0YXJ0LCB3ZSBuZWVkIHRvIGNvbnZlcnQgdGhlIGxhbmQgdXNlIG1hcCB0byB0aGUgc2FtZSAxKSByZXNvbHV0aW9uLCBhbmQgMikgZXh0ZW50IG9mIHRoZSByYWRhciBQUElzIGFzIHdlIGNhbiB0aGVuIHNpbXBseSAnb3ZlcmxheScgYm90aCByYXN0ZXJzIG9uIHRvcCBvZiBlYWNoIG90aGVyIGFuZCBkbyBjYWxjdWxhdGlvbnMuIFdlIGxvYWQgdGhlIFBQSXMgYW5kIGV4dHJhY3QgdGhlIENSUyBpbmZvcm1hdGlvbiBjb250YWluZWQgaW4gdGhlIGBwcm9qNGAgc3RyaW5ncy4KCmBgYHtyIGxvYWRfcHBpc19hbmRfcHJvajR9CnBwaV9ocncgPC0gcmVhZFJEUygiZGF0YS9wcm9jZXNzZWQvY29ycmVjdGVkX3BwaV9ocncuUkRTIikKcHBpX2RobCA8LSByZWFkUkRTKCJkYXRhL3Byb2Nlc3NlZC9jb3JyZWN0ZWRfcHBpX2RobC5SRFMiKQoKcHBpX3Byb2o0X2hydyA8LSBwcGlfaHJ3JGRhdGFAcHJvajRzdHJpbmcKcHBpX3Byb2o0X2RobCA8LSBwcGlfZGhsJGRhdGFAcHJvajRzdHJpbmcKYGBgCgpBbmQgd2UgbG9hZCBhbmQgcHJlcGFyZSB0aGUgbGFuZCB1c2UgbWFwIGl0J3MgYWxsIGFib3V0LiBUbyBhaWQgdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MsIHdlIGFsc28gbG9hZCB0aGUgbGFuZCB1c2UgY2xhc3NlcyBjb250YWluZWQgaW4gdGhlIGVudGlyZSBgQ0xDMjAxOGAgZGF0YXNldCwgb3RoZXJ3aXNlIHRoZSBjbGFzc2VzIHdpbGwgcmVtYWluIGFub255bW91cyBudW1iZXJzLgoKYGBge3IgbG9hZF9sYW5kdXNlX21hcCwgd2FybmluZz1GQUxTRX0KbGFuZHVzZSA8LSByYXN0ZXIoImRhdGEvcmF3L2xhbmR1c2UvY2xjMjAxOF9jbGMyMDE4X3YyMDE4XzIwX3Jhc3RlcjEwMG0vQ0xDMjAxOF9DTEMyMDE4X1YyMDE4XzIwLnRpZiIpCmxhbmR1c2VfY2xhc3NlcyA8LSByZWFkLmNzdigiZGF0YS9yYXcvbGFuZHVzZS9jbGMyMDE4X2NsYzIwMThfdjIwMThfMjBfcmFzdGVyMTAwbS9MZWdlbmQvQ0xDMjAxOF9DTEMyMDE4X1YyMDE4XzIwX1FHSVMudHh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoImxhbmR1c2VfaWQiLCAiciIsICJnIiwgImIiLCAieCIsICJsYW5kdXNlX2NsYXNzIikpWywgYygibGFuZHVzZV9pZCIsICJsYW5kdXNlX2NsYXNzIildCmBgYAoKIyMjIENyb3BwaW5nIHRoZSBsYW5kIHVzZSBtYXAKCkFzIHRoZSBgQ0xDMjAxOGAgZGF0YXNldCBpcyBzbyBsYXJnZSBpdCBkb2VzIG5vdCBmaXQgaW4gbWVtb3J5IGF0IGFsbCBpbiB0aGUgc3RlcHMgYmVsb3csIHNvIHdlIGhhdmUgdG8gY3JvcCB0aGUgcmFzdGVyIGRhdGFzZXQgZm9yIGZ1cnRoZXIgcHJvY2Vzc2luZy4gRXZlbiB0aGVuLCBpdCBzdGlsbCByZXF1aXJlcyBhIGJlZWZ5IGNvbXB1dGVyIHRvIHByb2Nlc3MgdGhlc2UgZmlsZXMuIEZpcnN0IHdlIGNhbGN1bGF0ZSBhIGJvdW5kaW5nIGJveCBmb3IgdGhlIGxhbmR1c2UgcmFzdGVyIGJhc2VkIG9uIHRoZSBib3VuZGluZyBib3hlcyBvZiB0aGUgcmFkYXIgZGF0YS4KCmBgYHtyIGNhbGN1bGF0ZV9ib3VuZGluZ19ib3hlc30KcGFkZGluZyA8LSAyNTAwMCAgIyBQYWRkaW5nIGluIG0gdG8gbWFrZSBzdXJlIHdlIGNyb3Agb3V0IG9mIHRoZSBsYW5kIHVzZSBtYXAgd2l0aCBhIHdpZGUgbWFyZ2luIHRvIGNvbXBlbnNhdGUgZm9yIGVkZ2UtZWZmZWN0cyBsYXRlcgpiYm94X21ldGVycyA8LSBhYnMocHBpX2RobCRkYXRhQGJib3hbWzFdXSkgKyBwYWRkaW5nICAjIEFzc3VtaW5nIHRoZSBQUEkgcmFuZ2Ugb2YgREhMIGFuZCBIUlcgYXJlIHRoZSBzYW1lCgpiYm94X2hydyA8LSBzdF9iYm94KGMoeG1pbiA9IC1iYm94X21ldGVycywgeW1pbiA9IC1iYm94X21ldGVycywgeG1heCA9IGJib3hfbWV0ZXJzLCB5bWF4ID0gYmJveF9tZXRlcnMpLCBjcnMgPSBwcGlfcHJvajRfaHJ3KQpiYm94X2RobCA8LSBzdF9iYm94KGMoeG1pbiA9IC1iYm94X21ldGVycywgeW1pbiA9IC1iYm94X21ldGVycywgeG1heCA9IGJib3hfbWV0ZXJzLCB5bWF4ID0gYmJveF9tZXRlcnMpLCBjcnMgPSBwcGlfcHJvajRfZGhsKQoKYmJveF9ocncgJT4lCiAgc3RfYXNfc2ZjKCkgJT4lCiAgc3RfdHJhbnNmb3JtKGNycyhsYW5kdXNlKSkgJT4lCiAgc3RfYmJveCAtPiBiYm94X2xhbmR1c2VfaHJ3CgpiYm94X2RobCAlPiUKICBzdF9hc19zZmMoKSAlPiUKICBzdF90cmFuc2Zvcm0oY3JzKGxhbmR1c2UpKSAlPiUKICBzdF9iYm94IC0+IGJib3hfbGFuZHVzZV9kaGwKYGBgCgpXZSBjYW4gbm93IGNyb3AgYW5kIHBsb3QgdGhlIGxhbmQgdXNlIG1hcHMgY2VudGVyZWQgb24gdGhlIHJhZGFyIHNpdGVzLCB3aXRoIGEgYHIgcGFkZGluZ2AgbWV0ZXIgcGFkZGluZyBzdXJyb3VuZGluZyB0aGUgZXh0ZW50IG9mIHRoZSByYWRhciBkYXRhLgoKYGBge3IgY3JvcF9sYW5kdXNlX21hcHN9CmV4dF9ocncgPC0gZXh0ZW50KGMoYmJveF9sYW5kdXNlX2hyd1sxXSwgYmJveF9sYW5kdXNlX2hyd1szXSwgYmJveF9sYW5kdXNlX2hyd1syXSwgYmJveF9sYW5kdXNlX2hyd1s0XSkpCmV4dF9kaGwgPC0gZXh0ZW50KGMoYmJveF9sYW5kdXNlX2RobFsxXSwgYmJveF9sYW5kdXNlX2RobFszXSwgYmJveF9sYW5kdXNlX2RobFsyXSwgYmJveF9sYW5kdXNlX2RobFs0XSkpCgpsYW5kdXNlX2Nyb3BfaHJ3IDwtIGNyb3AobGFuZHVzZSwgZXh0X2hydykKbGFuZHVzZV9jcm9wX2RobCA8LSBjcm9wKGxhbmR1c2UsIGV4dF9kaGwpCmBgYAoKQW5kIHBsb3QgdGhlIHJlc3VsdDoKCmBgYHtyIHBsb3RfY3JvcHBlZF9sYW5kdXNlX21hcHN9CnBhcihwdHkgPSAicyIsIG1mcm93ID0gYygxLCAyKSkKaW1hZ2UobGFuZHVzZV9jcm9wX2hydywgbWFpbiA9ICJIZXJ3aWpuZW4iKQppbWFnZShsYW5kdXNlX2Nyb3BfZGhsLCBtYWluID0gIkRlbiBIZWxkZXIiKQpgYGAKCkFwcGFyYW50bHkgbGFyZ2Ugc3dhdGhlcyBvZiB0aGUgTm9ydGggU2VhIGFyZSBzZXQgdG8gYE5hTmAsIHNvIHdlIGJldHRlciBjb252ZXJ0IHRob3NlIHRvICdTZWEgYW5kIG9jZWFuJyBhcyB3ZWxsLgoKYGBge3IgcGxvdF9jcm9wcGVkX2xhbmR1c2VfbWFwc19maWxsZWRfTkFzfQpzZWFfaWQgPC0gbWF0Y2goJ1NlYSBhbmQgb2NlYW4nLCBsYW5kdXNlX2NsYXNzZXMkbGFuZHVzZV9jbGFzcykKbGFuZHVzZV9jcm9wX2hyd1tpcy5uYShsYW5kdXNlX2Nyb3BfaHJ3KV0gPC0gbGFuZHVzZV9jbGFzc2VzW3NlYV9pZCwgImxhbmR1c2VfaWQiXQpsYW5kdXNlX2Nyb3BfZGhsW2lzLm5hKGxhbmR1c2VfY3JvcF9kaGwpXSA8LSBsYW5kdXNlX2NsYXNzZXNbc2VhX2lkLCAibGFuZHVzZV9pZCJdCgpwYXIocHR5ID0gInMiLCBtZnJvdyA9IGMoMSwgMikpCmltYWdlKGxhbmR1c2VfY3JvcF9ocncsIG1haW4gPSAiSGVyd2lqbmVuIikKaW1hZ2UobGFuZHVzZV9jcm9wX2RobCwgbWFpbiA9ICJEZW4gSGVsZGVyIikKYGBgCgojIyMgUmVwcm9qZWN0aW5nIHRoZSBsYW5kIHVzZSBtYXAKCk5vdyB0aGF0IHRoZSBsYW5kIHVzZSBtYXAgaXMgY3JvcHBlZCwgd2UgY2FuIHN0YXJ0IHRoZSByZXByb2plY3RpbmcgdG8gdGhlIENSUyBvZiB0aGUgcmFkYXIgUFBJLiBBcyB3ZSdyZSBkZWFsaW5nIHdpdGggY2F0ZWdvcmljYWwgZGF0YSwgd2Ugc2V0IGBtZXRob2QgPSAibmdiImAgdG8gdXNlIG5lYXJlc3QgbmVpZ2hib3VyIGludGVycG9sYXRpb24uCgpgYGB7ciByZXByb2plY3RfbGFuZHVzZV9tYXBzLCB3YXJuaW5nPUZBTFNFfQpsYW5kdXNlX2hyd19yZXByb2plY3RlZCA8LSBwcm9qZWN0UmFzdGVyKGxhbmR1c2VfY3JvcF9ocncsIGNycyA9IHBwaV9wcm9qNF9ocncsIG1ldGhvZCA9ICJuZ2IiKQpsYW5kdXNlX2RobF9yZXByb2plY3RlZCA8LSBwcm9qZWN0UmFzdGVyKGxhbmR1c2VfY3JvcF9kaGwsIGNycyA9IHBwaV9wcm9qNF9kaGwsIG1ldGhvZCA9ICJuZ2IiKQpsZXZlbHMobGFuZHVzZV9ocndfcmVwcm9qZWN0ZWQpIDwtIGxldmVscyhsYW5kdXNlX2Nyb3BfaHJ3KQpsZXZlbHMobGFuZHVzZV9kaGxfcmVwcm9qZWN0ZWQpIDwtIGxldmVscyhsYW5kdXNlX2Nyb3BfZGhsKQpgYGAKCklmIHRoZSByZXByb2plY3Rpb24gd2VudCBzdWNjZXNzZnVsLCB0aGUgQ1JTIG9mIHRoZSByZXByb2plY3RlZCBsYW5kIHVzZSBtYXAgYW5kIHRoZSByYWRhciBQUEkgc2hvdWxkIGJlIHRoZSBzYW1lLgoKYGBge3IgY29tcGFyZV9yZXByb2plY3RlZF9sYW5kdXNlX21hcHN9CmNvbXBhcmVDUlMocHBpX2hydyRkYXRhQHByb2o0c3RyaW5nLCBsYW5kdXNlX2hyd19yZXByb2plY3RlZEBjcnMpCmNvbXBhcmVDUlMocHBpX2RobCRkYXRhQHByb2o0c3RyaW5nLCBsYW5kdXNlX2RobF9yZXByb2plY3RlZEBjcnMpCmBgYAoKQXBwYXJlbnRseSB0aGF0IGlzIHRoZSBjYXNlLgoKIyMjIFJlc2FtcGxpbmcgdGhlIGxhbmQgdXNlIG1hcCB0byBhIGxvd2VyIHJlc29sdXRpb24KClRoZSBjZWxsc2l6ZSBvZiB0aGUgUFBJcyBpcyBgciBwcGlfZGhsJGRhdGFAZ3JpZEBjZWxsc2l6ZWB4YHIgcHBpX2RobCRkYXRhQGdyaWRAY2VsbHNpemVgIG1ldGVycywgYnV0IHRoZSBsYW5kIHVzZSBtYXAgaXMgbXVjaCBtb3JlIGZpbmVseSBkZXRhaWxlZCAofjEwMHgxMDBtKSwgc28gd2UgbmVlZCB0byByZXNhbXBsZSB0aGUgbGF0dGVyIHRvIGRlcml2ZSBhIGxhbmQgdXNlIG1hcCBhdCBhIGByIHBwaV9kaGwkZGF0YUBncmlkQGNlbGxzaXplYHhgciBwcGlfZGhsJGRhdGFAZ3JpZEBjZWxsc2l6ZWAgbWV0ZXIgcmVzb2x1dGlvbiBhcyB3ZWxsLgoKU2ltcGxlIG5lYXJlc3QgbmVpZ2hib3VyIHJlc2FtcGxpbmcgd2FzIHRlc3RlZCwgYnV0IHJlc3VsdGVkIGluIGxhcmdlIHBhcnRzIG9mIGZhaXJseSBzbWFsbCB3YXRlciBib2RpZXMgYW5kIGVzcGVjaWFsbHkgdGhlIHJpdmVycyAnZGlzYXBwZWFyaW5nJyBmcm9tIHRoZSBtYXAsIHdoZXJlYXMgdGhlc2UgcmVwcmVzZW50IGltcG9ydGFudCBoYWJpdGF0cyBmb3IgYmlyZHMgdG8gZm9yYWdlIGFuZCByb29zdC4gU28gaW5zdGVhZCwgbWFqb3JpdHkgcmVzYW1wbGluZywgdHdlYWtlZCB0byByZXRhaW4gbW9yZSBvZiB0aGUgYWZvcmVtZW50aW9uZWQgd2F0ZXIgZmVhdHVyZXMgd2FzIHVzZWQgdG8gcmVzaXplIHRoZSBsYW5kIHVzZSByYXN0ZXIgdG8gYW4gYXBwcm9wcmlhdGUgcmVzb2x1dGlvbi4gVGhlIHR3ZWFrIGNvbnN0aXR1dGVkIG9mIHNldHRpbmcgdGhlIHZhbHVlcyBvZiB3YXRlciBjb3Vyc2VzIGFuZCB3YXRlciBib2RpZXMgdG8gdGhlIG1heGltdW0gdmFsdWUgKHRvdGFsIGNvdmVyYWdlIG9mIHRoZSBhc3Nlc3NlZCB3aW5kb3cpIGlmIGhhbGYgb3IgbW9yZSBvZiB0aGUgYXNzZXNzZWQgd2luZG93IGNvbnRhaW5lZCBwaXhlbHMgY2xhc3NpZmllZCBhcyB3YXRlciBjb3Vyc2VzIG9yIHdhdGVyIGJvZGllcy4gVGhlIHF1YWxpdHkgb2YgdGhpcyByZXNhbXBsaW5nIHdhcyBhc3Nlc3NlZCB2aXN1YWxseS4KCmBgYHtyIGRlZmluZV9tYWpvcml0eV9yZXNhbXBsaW5nX3RlY2huaXF1ZX0KbWFqb3JpdHlfcmVzYW1wbGluZyA8LSBmdW5jdGlvbihyYXN0ZXIsIHJlZmVyZW5jZV9yYXN0ZXIsIG92ZXJ3cml0ZSA9IEZBTFNFKSB7CiAgdmFsdWVzIDwtIGMoc29ydCh1bmlxdWUoZ2V0VmFsdWVzKHJhc3RlcikpKSkKICAKICAjIGNsYXNzZXM6IG11bHRpZGltZW5zaW9uYWwgbG9naWNhbCBhcnJheSBmb3IgdGhlIGNsYXNzZXMgY29udGFpbmVkIHdpdGhpbiB0aGUgbGFuZCB1c2UgbWFwCiAgY2xhc3NlcyA8LSBsYXllcml6ZShyYXN0ZXIsIGZpbGVuYW1lID0gcGFzdGUoImRhdGEvcHJvY2Vzc2VkL2xhbmR1c2UvbGF5ZXJpemUvIiwgc3Vic3RpdHV0ZShyYXN0ZXIpLCBzZXAgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAicmFzdGVyIiwgYnlsYXllciA9IFRSVUUsIGNsYXNzZXMgPSB2YWx1ZXMsIG92ZXJ3cml0ZSA9IG92ZXJ3cml0ZSkKICAjIGZhY3RvcjogbnIgb2YgY2VsbHMgaW4gYm90aCBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBkaXJlY3Rpb24gdG8gYWdncmVnYXRlCiAgZmFjdG9yIDwtIHJvdW5kKGRpbShyYXN0ZXIpWzE6Ml0gLyBkaW0ocmVmZXJlbmNlX3Jhc3RlcilbMToyXSkKICAjIGFnZzogYWdncmVnYXRlZCB2ZXJzaW9uIG9mIGNsYXNzZXMgKGFnZ3JlZ2F0aW9uIGZhY3RvciBkZWZpbmVkIGJ5IGZhY3RvcikgY29udGFpbmluZyBtYXggdmFsdWVzIHBlciBjbGFzcywgc28gMSBpZiBhIGNsYXNzIGlzIHByZXNlbnQKICBhZ2cgPC0gYWdncmVnYXRlKGNsYXNzZXMsIGZhY3RvciwgbmEucm0gPSBUUlVFLCBmdW4gPSBtYXgpCiAgIyB4OiByZXNhbXBsZWQgYWdnLCBub3cgcm91Z2hseSBjb250YWluaW5nIGxhbmQgY292ZXJhZ2UgcGVyY2VudGFnZXMuIE1ldGhvZCBtdXN0IGJlIGJpbGluZWFyLCBvdGhlcndpc2UgYXJiaXRyYXJ5IGNob2ljZXMga2VlcCBiZWluZyBtYWRlLgogIHggPC0gcmVzYW1wbGUoYWdnLCByZWZlcmVuY2VfcmFzdGVyKQoKICAjIFR3ZWFrIHRoZSBpbXBvcnRhbmNlIG9mIHdhdGVyIGNvdXJzZXMgYW5kIGJvZGllcwogIHdhdGVyX2NvdXJzZXNfaWQgPC0gbWF0Y2goNTExLCB2YWx1ZXMpCiAgd2F0ZXJfYm9kaWVzX2lkIDwtIG1hdGNoKDUxMiwgdmFsdWVzKQogIHhbW3dhdGVyX2NvdXJzZXNfaWRdXVt4W1t3YXRlcl9jb3Vyc2VzX2lkXV0gPj0gMC41XSA8LSAxLjEKICB4W1t3YXRlcl9ib2RpZXNfaWRdXVt4W1t3YXRlcl9ib2RpZXNfaWRdXSA+PSAwLjVdIDwtIDEuMQoKICAjIEZsYXR0ZW4geCBpbiB5CiAgeSA8LSB3aGljaC5tYXgoeCkKCiAgIyBSZWNsYXNzaWZ5IGJhY2sgdG8gb3JpZ2luYWwgdmFsdWVzCiAgdmFsdWVzIDwtIGRhdGEuZnJhbWUoc2VxKDEsIGxlbmd0aCh2YWx1ZXMpKSwgdmFsdWVzKQogIGNvbG5hbWVzKHZhbHVlcykgPC0gYygiaXMiLCAiYmVjb21lcyIpCiAgeSA8LSByZWNsYXNzaWZ5KHksIHZhbHVlcykKCiAgcmV0dXJuKHkpCn0KYGBgCgpgYGB7ciByZXNpemVfbGFuZHVzZV93aXRoX21ham9yaXR5X3Jlc2FtcGxpbmd9CmxhbmR1c2VfaHJ3IDwtIG1ham9yaXR5X3Jlc2FtcGxpbmcobGFuZHVzZV9ocndfcmVwcm9qZWN0ZWQsIGFzKHBwaV9ocnckZGF0YSwgIlJhc3RlckxheWVyIiksIG92ZXJ3cml0ZSA9IFRSVUUpCmxhbmR1c2VfZGhsIDwtIG1ham9yaXR5X3Jlc2FtcGxpbmcobGFuZHVzZV9kaGxfcmVwcm9qZWN0ZWQsIGFzKHBwaV9kaGwkZGF0YSwgIlJhc3RlckxheWVyIiksIG92ZXJ3cml0ZSA9IFRSVUUpCndyaXRlUmFzdGVyKGxhbmR1c2VfaHJ3LCAiZGF0YS9wcm9jZXNzZWQvbGFuZHVzZS9sYW5kdXNlX2hyd181MDBtLnRpZiIsIG92ZXJ3cml0ZSA9IFRSVUUpCndyaXRlUmFzdGVyKGxhbmR1c2VfZGhsLCAiZGF0YS9wcm9jZXNzZWQvbGFuZHVzZS9sYW5kdXNlX2RobF81MDBtLnRpZiIsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKQnkgbm93IHRoZSByZXNhbXBsZWQgbGFuZCB1c2UgcmFzdGVyIHNob3VsZCBiZSB2ZXJ5IHNpbWlsYXIgdG8gdGhlIFBQSSByYXN0ZXIsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiDigJQgb2YgY291cnNlIOKAlCB0aGUgdmFsdWVzIGNvbnRhaW5lZCB3aXRoaW4uCgpgYGB7ciBjb21wYXJlX3Jlc2FtcGxlZF9sYW5kdXNlX3Jhc3RlcnN9CmNvbXBhcmVSYXN0ZXIobGFuZHVzZV9ocncsIGFzKHBwaV9ocnckZGF0YSwgIlJhc3RlckxheWVyIiksIGV4dGVudCA9IFRSVUUsIHJvd2NvbCA9IFRSVUUsIGNycyA9IFRSVUUsIHJlcyA9IFRSVUUsIG9yaWcgPSBUUlVFKQpjb21wYXJlUmFzdGVyKGxhbmR1c2VfZGhsLCBhcyhwcGlfZGhsJGRhdGEsICJSYXN0ZXJMYXllciIpLCBleHRlbnQgPSBUUlVFLCByb3djb2wgPSBUUlVFLCBjcnMgPSBUUlVFLCByZXMgPSBUUlVFLCBvcmlnID0gVFJVRSkKYGBgCgpPaywgbGV0J3Mgc2F2ZSBhIGNvcHkgb2Ygd2hhdCB3ZSBoYXZlIHNvIGZhci4KYGBge3Igc2F2ZV9yZXNhbXBsZWRfcmFzdGVyc30Kc2F2ZVJEUyhsYW5kdXNlX2hydywgImRhdGEvcHJvY2Vzc2VkL2xhbmR1c2UvbGFuZHVzZV9ocncuUkRTIikKc2F2ZVJEUyhsYW5kdXNlX2RobCwgImRhdGEvcHJvY2Vzc2VkL2xhbmR1c2UvbGFuZHVzZV9kaGwuUkRTIikKYGBgCgoKIyMgQWRkaW5nIGxhbmQgdXNlIGNsYXNzaWZpY2F0aW9ucyB0byB0aGUgUFBJcwoKV2l0aCB0aGUgbGFuZCB1c2UgcmFzdGVycyBvdmVybGFwcGluZyBleGFjdGx5IHdpdGggdGhlIFBQSXMsIHdlIGNhbiBzaW1wbHkgZXh0cmFjdCB0aGUgdmFsdWVzIG9mIHRoZSByZXNhbXBsZWQgbGFuZCB1c2UgcmFzdGVycyBhbmQgYWRkIHRoZXNlIGFzIGFkZGl0aW9uYWwgcGFyYW1ldGVycyB0byB0aGUgUFBJcy4gQmVzaWRlcyB0aGUgY29kZSB1c2VkLCB3ZSB3aWxsIGFsc28gYWRkIGEgZGVzY3JpcHRpb24gb2YgdGhlIGNvZGVzLgoKYGBge3Igc3RvcmVfY2xhc3NpZmljYXRpb25fdmFsdWVzX2luX3BwaXN9CmxhbmR1c2VfaHJ3IDwtIHJlYWRSRFMoImRhdGEvcHJvY2Vzc2VkL2xhbmR1c2UvbGFuZHVzZV9ocncuUkRTIikKbGFuZHVzZV9kaGwgPC0gcmVhZFJEUygiZGF0YS9wcm9jZXNzZWQvbGFuZHVzZS9sYW5kdXNlX2RobC5SRFMiKQoKdmFsdWVzX2hydyA8LSBhcy5kYXRhLmZyYW1lKGxhbmR1c2VfaHJ3QGRhdGFAdmFsdWVzKQp2YWx1ZXNfZGhsIDwtIGFzLmRhdGEuZnJhbWUobGFuZHVzZV9kaGxAZGF0YUB2YWx1ZXMpCgpwcGlfaHJ3JGRhdGEkbGFuZHVzZSA8LSBhcy5udW1lcmljKHVubGlzdCh2YWx1ZXNfaHJ3KSkKcHBpX2RobCRkYXRhJGxhbmR1c2UgPC0gYXMubnVtZXJpYyh1bmxpc3QodmFsdWVzX2RobCkpCgpwcGlfaHJ3JGRhdGFAZGF0YSA8LSBwcGlfaHJ3JGRhdGFAZGF0YSAlPiUKICBsZWZ0X2pvaW4obGFuZHVzZV9jbGFzc2VzLCBieSA9IGMoImxhbmR1c2UiID0gImxhbmR1c2VfaWQiKSkKcHBpX2hydyRkYXRhJElEIDwtIE5VTEwKCnBwaV9kaGwkZGF0YUBkYXRhIDwtIHBwaV9kaGwkZGF0YUBkYXRhICU+JQogIGxlZnRfam9pbihsYW5kdXNlX2NsYXNzZXMsIGJ5ID0gYygibGFuZHVzZSIgPSAibGFuZHVzZV9pZCIpKQpwcGlfZGhsJGRhdGEkSUQgPC0gTlVMTApgYGAKCiMjIENsYXNzaWZ5IGxhbmQgdXNlIHR5cGVzIGluIFVyYmFuIHZzLiBSdXJhbAoKQXMgd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGVmZmVjdCB1cmJhbml6YXRpb24gaGFzIG9uIHRoZSB0YWtlLW9mZiBkZWNpc2lvbiBvZiBiaXJkcyBkdXJpbmcgdGhlIGZpcmV3b3JrcyBldmVudCwgaXQgaXMgdXNlZnVsIHRvIGNhdGVnb3Jpc2UgdGhlIGxhbmQgdXNlIHR5cGVzIHdlIGhhdmUgZm91bmQgc28gZmFyIGFzIGVpdGhlciAndXJiYW4nIG9yICdydXJhbCcgYXMgd2VsbC4KCmBgYHtyIGNsYXNzaWZ5X2xhbmRfdXNlX3VyYmFuX3ZzX3J1cmFsfQp1cmJhbl9ocncgPC0gcHBpX2hydyRkYXRhQGRhdGEkbGFuZHVzZSA8IDIwMApwcGlfaHJ3JGRhdGEkdHlwZSA8LSByZXAoInJ1cmFsIiwgbnJvdyhwcGlfaHJ3JGRhdGEpKQpwcGlfaHJ3JGRhdGEkdHlwZVt1cmJhbl9ocnddIDwtICJ1cmJhbiIKcHBpX2hydyRkYXRhJHR5cGUgPC0gYXMuZmFjdG9yKHBwaV9ocnckZGF0YSR0eXBlKQoKdXJiYW5fZGhsIDwtIHBwaV9kaGwkZGF0YUBkYXRhJGxhbmR1c2UgPCAyMDAKcHBpX2RobCRkYXRhJHR5cGUgPC0gcmVwKCJydXJhbCIsIG5yb3cocHBpX2RobCRkYXRhKSkKcHBpX2RobCRkYXRhJHR5cGVbdXJiYW5fZGhsXSA8LSAidXJiYW4iCnBwaV9kaGwkZGF0YSR0eXBlIDwtIGFzLmZhY3RvcihwcGlfZGhsJGRhdGEkdHlwZSkKYGBgCgojIyBDYWxjdWxhdGUgZGlzdGFuY2UgdG8gbmVhcmVzdCB1cmJhbiBhcmVhCgpGb3IgZXZlcnkgY2VsbCBvbiB0aGUgcmFzdGVyIHRoYXQgaXMgbm90IGEgY2VsbCB3ZSBoYXZlIGp1c3QgY2xhc3NpZmllZCBhcyAndXJiYW4nIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAoaW4gbWV0ZXJzKSB0byB0aGUgbmVhcmVzdCBjZWxsIGNsYXNzaWZpZWQgYXMgJ3VyYmFuJy4KCmBgYHtyIGNhbGN1bGF0ZV9kaXN0YW5jZV90b191cmJhbl9hcmVhc30KdXJiYW5faHJ3IDwtIHBwaV9ocnckZGF0YVssICwgInR5cGUiLCBkcm9wID0gRkFMU0VdCnVyYmFuX2hydyR0eXBlW3VyYmFuX2hydyR0eXBlID09ICJydXJhbCJdIDwtIE5hTgpkaXN0YW5jZV90b191cmJhbl9ocncgPC0gZGlzdGFuY2UoYXModXJiYW5faHJ3LCAnUmFzdGVyTGF5ZXInKSkKCnVyYmFuX2RobCA8LSBwcGlfZGhsJGRhdGFbLCAsICJ0eXBlIiwgZHJvcCA9IEZBTFNFXQp1cmJhbl9kaGwkdHlwZVt1cmJhbl9kaGwkdHlwZSA9PSAicnVyYWwiXSA8LSBOYU4KZGlzdGFuY2VfdG9fdXJiYW5fZGhsIDwtIGRpc3RhbmNlKGFzKHVyYmFuX2RobCwgJ1Jhc3RlckxheWVyJykpCgp3cml0ZVJhc3RlcihkaXN0YW5jZV90b191cmJhbl9ocncsICJkYXRhL3Byb2Nlc3NlZC9sYW5kdXNlL2Rpc3RfdXJiYW5faHJ3XzUwMG0udGlmIiwgb3ZlcndyaXRlID0gVFJVRSkKd3JpdGVSYXN0ZXIoZGlzdGFuY2VfdG9fdXJiYW5fZGhsLCAiZGF0YS9wcm9jZXNzZWQvbGFuZHVzZS9kaXN0X3VyYmFuX2RobF81MDBtLnRpZiIsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKQW5kIHdlIGFkZCB0aGVzZSB2YWx1ZXMgdG8gdGhlIFBQSXMgYWdhaW4uCgpgYGB7ciBzdG9yZV9kaXN0YW5jZV90b191cmJhbl9hcmVhc19pbl9wcGl9CnZhbHVlc19kaXN0X3VyYmFuX2hydyA8LSBhcy5kYXRhLmZyYW1lKGRpc3RhbmNlX3RvX3VyYmFuX2hyd0BkYXRhQHZhbHVlcykKdmFsdWVzX2Rpc3RfdXJiYW5fZGhsIDwtIGFzLmRhdGEuZnJhbWUoZGlzdGFuY2VfdG9fdXJiYW5fZGhsQGRhdGFAdmFsdWVzKQoKcHBpX2hydyRkYXRhJGRpc3RfdXJiYW4gPC0gYXMubnVtZXJpYyh1bmxpc3QodmFsdWVzX2Rpc3RfdXJiYW5faHJ3KSkKcHBpX2RobCRkYXRhJGRpc3RfdXJiYW4gPC0gYXMubnVtZXJpYyh1bmxpc3QodmFsdWVzX2Rpc3RfdXJiYW5fZGhsKSkKYGBgCgojIyBUZXN0aW5nCgpMZXQncyBtYWtlIGEgZmV3IHBsb3RzIHRvIHNlZSBpZiB3aGF0IHdlIGhhdmUgbm93IGdhdGhlcmVkIGFzIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gbWFrZXMgYW55IHNlbnNlCgojIyMgUGxvdHRpbmcgdG90YWwgVklSIHBlciBsYW5kIHVzZSB0eXBlCgpgYGB7ciB0b3RhbF92aXJfdnNfbGFuZF91c2VfdHlwZX0KZGF0YV9ocncgPC0gcHBpX2hydyRkYXRhQGRhdGEgJT4lCiAgZmlsdGVyKGxhbmR1c2VfY2xhc3MgIT0gIkxhbmQgcHJpbmNpcGFsbHkgb2NjdXBpZWQgYnkgYWdyaWN1bHR1cmUgd2l0aCBzaWduaWZpY2FudCBhcmVhcyBvZiBuYXR1cmFsIHZlZ2V0YXRpb24iICYgbGFuZHVzZV9jbGFzcyAhPSAiU2VhIGFuZCBvY2VhbiIpICU+JQogIGdyb3VwX2J5KGxhbmR1c2VfY2xhc3MpICU+JQogIGFkZF9jb3VudCgpICU+JQogIG11dGF0ZSh0b3RhbF9sYW5kdXNlX2NsYXNzID0gc3VtKHZpciksIHRvdGFsX3Byb3BfbGFuZHVzZV9jbGFzcyA9IHN1bSh2aXIpIC8gbikKCmRhdGFfZGhsIDwtIHBwaV9kaGwkZGF0YUBkYXRhICU+JQogIGZpbHRlcihsYW5kdXNlX2NsYXNzICE9ICJMYW5kIHByaW5jaXBhbGx5IG9jY3VwaWVkIGJ5IGFncmljdWx0dXJlIHdpdGggc2lnbmlmaWNhbnQgYXJlYXMgb2YgbmF0dXJhbCB2ZWdldGF0aW9uIiAmIGxhbmR1c2VfY2xhc3MgIT0gIlNlYSBhbmQgb2NlYW4iKSAlPiUKICBncm91cF9ieShsYW5kdXNlX2NsYXNzKSAlPiUKICBhZGRfY291bnQoKSAlPiUKICBtdXRhdGUodG90YWxfbGFuZHVzZV9jbGFzcyA9IHN1bSh2aXIpLCB0b3RhbF9wcm9wX2xhbmR1c2VfY2xhc3MgPSBzdW0odmlyKSAvIG4pCgpnZ3Bsb3QoZGF0YV9ocncsIGFlcyhsYW5kdXNlX2NsYXNzLCB0b3RhbF9wcm9wX2xhbmR1c2VfY2xhc3MsIGZpbGwgPSB0eXBlKSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJIZXJ3aWpuZW4iKQpnZ3Bsb3QoZGF0YV9kaGwsIGFlcyhsYW5kdXNlX2NsYXNzLCB0b3RhbF9wcm9wX2xhbmR1c2VfY2xhc3MsIGZpbGwgPSB0eXBlKSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJEZW4gSGVsZGVyIikKYGBgCgojIyMgUGxvdHRpbmcgYSBkaXN0YW5jZSB0byB1cmJhbiBhcmVhcyBlZmZlY3QgZm9yIFZJUgoKYGBge3IgZGlzdGFuY2VfdG9fdXJiYW5fdnNfdmlyLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QocHBpX2RobCRkYXRhQGRhdGEsIGFlcyh4ID0gZGlzdF91cmJhbiwgeSA9IHZpciwgY29sb3VyID0gbGFuZHVzZV9jbGFzcykpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyB4bGltKGMoMCwgMTAwMDApKSArIHlsaW0oYygwLCA1MDAwMDApKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKZ2dwbG90KHBwaV9ocnckZGF0YUBkYXRhLCBhZXMoeCA9IGRpc3RfdXJiYW4sIHkgPSB2aXIsIGNvbG91ciA9IGxhbmR1c2VfY2xhc3MpKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsgeGxpbShjKDAsIDEwMDAwKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIyMjIFBsb3R0aW5nIGl0IGFsbCBvbiBhbiBpbnRlcmFjdGl2ZSBtYXAKCiMjIyMgSW50ZXJhY3RpdmUgbWFwIG9mIEhlcndpam5lbgpgYGB7ciBpbnRlcmFjdGl2ZV9tYXBfaGVyd2lqbmVufQptYXB2aWV3KHBwaV9ocnckZGF0YVssICwgYygibGFuZHVzZSIsICJkaXN0X3VyYmFuIildLCBhbHBoYS5yZWdpb25zID0gMC42LCBjb2wucmVnaW9ucyA9IGluZmVybm8sIG1heHBpeGVscz0yMDAwMDAwLAogICAgICAgIG5hLmNvbG9yID0gIiMwMDAwMDAwMCIsIG1hcC50eXBlcyA9IGMoIkNhcnRvREIuUG9zaXRyb24iLCAiQ2FydG9EQi5EYXJrTWF0dGVyIiwgIkVzcmkuV29ybGRJbWFnZXJ5IiksCiAgICAgICAgbGF5ZXIubmFtZSA9IGMoImxhbmR1c2UiLCAiZGlzdF91cmJhbiIpKQpgYGAKCgojIyMjIEludGVyYWN0aXZlIG1hcCBvZiBEZW4gSGVsZGVyCmBgYHtyIGludGVyYWN0aXZlX21hcF9kZW5faGVsZGVyfQptYXB2aWV3KHBwaV9kaGwkZGF0YVssICwgYygibGFuZHVzZSIsICJkaXN0X3VyYmFuIildLCBhbHBoYS5yZWdpb25zID0gMC42LCBjb2wucmVnaW9ucyA9IGluZmVybm8sIG1heHBpeGVscz0yMDAwMDAwLAogICAgICAgIG5hLmNvbG9yID0gIiMwMDAwMDAwMCIsIG1hcC50eXBlcyA9IGMoIkNhcnRvREIuUG9zaXRyb24iLCAiQ2FydG9EQi5EYXJrTWF0dGVyIiwgIkVzcmkuV29ybGRJbWFnZXJ5IiksCiAgICAgICAgbGF5ZXIubmFtZSA9IGMoImxhbmR1c2UiLCAiZGlzdF91cmJhbiIpKQpgYGAKCg==